static void ept_handle_violation(unsigned long qualification, paddr_t gpa)
{
- if ( unlikely(((qualification >> 7) & 0x3) != 0x3) )
+ unsigned long gla_validity = qualification & EPT_GLA_VALIDITY_MASK;
+ struct domain *d = current->domain;
+ unsigned long gfn = gpa >> PAGE_SHIFT;
+ mfn_t mfn;
+ p2m_type_t t;
+
+ if ( unlikely(qualification & EPT_GAW_VIOLATION) )
+ {
+ gdprintk(XENLOG_ERR, "EPT violation: guest physical address %"PRIpaddr
+ " exceeded its width limit.\n", gpa);
+ goto crash;
+ }
+
+ if ( unlikely(gla_validity == EPT_GLA_VALIDITY_RSVD) ||
+ unlikely(gla_validity == EPT_GLA_VALIDITY_PDPTR_LOAD) )
{
- domain_crash(current->domain);
+ gdprintk(XENLOG_ERR, "EPT violation: reserved bit or "
+ "pdptr load violation.\n");
+ goto crash;
+ }
+
+ mfn = gfn_to_mfn(d, gfn, &t);
+ if ( p2m_is_ram(t) && paging_mode_log_dirty(d) )
+ {
+ paging_mark_dirty(d, mfn_x(mfn));
+ p2m_change_type(d, gfn, p2m_ram_logdirty, p2m_ram_rw);
+ flush_tlb_mask(d->domain_dirty_cpumask);
return;
}
+ /* This can only happen in log-dirty mode, writing back A/D bits. */
+ if ( unlikely(gla_validity == EPT_GLA_VALIDITY_GPT_WALK) )
+ goto crash;
+
+ ASSERT(gla_validity == EPT_GLA_VALIDITY_MATCH);
handle_mmio();
+
+ return;
+
+ crash:
+ domain_crash(d);
}
static void vmx_failed_vmentry(unsigned int exit_reason,
hap_unlock(d);
/* set l1e entries of P2M table to be read-only. */
- p2m_change_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
+ p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
flush_tlb_mask(d->domain_dirty_cpumask);
return 0;
}
hap_unlock(d);
/* set l1e entries of P2M table with normal mode */
- p2m_change_type_global(d, p2m_ram_logdirty, p2m_ram_rw);
+ p2m_change_entry_type_global(d, p2m_ram_logdirty, p2m_ram_rw);
return 0;
}
void hap_clean_dirty_bitmap(struct domain *d)
{
/* set l1e entries of P2M table to be read-only. */
- p2m_change_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
+ p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
flush_tlb_mask(d->domain_dirty_cpumask);
}
#include <asm/hvm/vmx/vmx.h>
#include <xen/iommu.h>
+static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type)
+{
+ switch(type)
+ {
+ case p2m_invalid:
+ case p2m_mmio_dm:
+ default:
+ return;
+ case p2m_ram_rw:
+ case p2m_mmio_direct:
+ entry->r = entry->w = entry->x = 1;
+ return;
+ case p2m_ram_logdirty:
+ case p2m_ram_ro:
+ entry->r = entry->x = 1;
+ entry->w = 0;
+ return;
+ }
+}
+
static int ept_next_level(struct domain *d, bool_t read_only,
ept_entry_t **table, unsigned long *gfn_remainder,
u32 shift)
ept_entry->avail2 = 0;
/* last step */
ept_entry->r = ept_entry->w = ept_entry->x = 1;
+ ept_p2m_type_to_flags(ept_entry, p2mt);
}
else
ept_entry->epte = 0;
index = gfn_remainder;
ept_entry = table + index;
- if ( (ept_entry->epte & 0x7) == 0x7 )
+ if ( ept_entry->avail1 != p2m_invalid )
{
- if ( ept_entry->avail1 != p2m_invalid )
- {
- *t = ept_entry->avail1;
- mfn = _mfn(ept_entry->mfn);
- }
+ *t = ept_entry->avail1;
+ mfn = _mfn(ept_entry->mfn);
}
out:
return ept_get_entry(current->domain, gfn, t);
}
+/* Walk the whole p2m table, changing any entries of the old type
+ * to the new type. This is used in hardware-assisted paging to
+ * quickly enable or diable log-dirty tracking */
+
+static void ept_change_entry_type_global(struct domain *d,
+ p2m_type_t ot, p2m_type_t nt)
+{
+ ept_entry_t *l4e, *l3e, *l2e, *l1e;
+ int i4, i3, i2, i1;
+
+ if ( pagetable_get_pfn(d->arch.phys_table) == 0 )
+ return;
+
+ BUG_ON(EPT_DEFAULT_GAW != 3);
+
+ l4e = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table)));
+ for (i4 = 0; i4 < EPT_PAGETABLE_ENTRIES; i4++ )
+ {
+ if ( !l4e[i4].epte || l4e[i4].sp_avail )
+ continue;
+ l3e = map_domain_page(l4e[i4].mfn);
+ for ( i3 = 0; i3 < EPT_PAGETABLE_ENTRIES; i3++ )
+ {
+ if ( !l3e[i3].epte || l3e[i3].sp_avail )
+ continue;
+ l2e = map_domain_page(l3e[i3].mfn);
+ for ( i2 = 0; i2 < EPT_PAGETABLE_ENTRIES; i2++ )
+ {
+ if ( !l2e[i2].epte || l2e[i2].sp_avail )
+ continue;
+ l1e = map_domain_page(l2e[i2].mfn);
+ for ( i1 = 0; i1 < EPT_PAGETABLE_ENTRIES; i1++ )
+ {
+ if ( !l1e[i1].epte )
+ continue;
+ if ( l1e[i1].avail1 != ot )
+ continue;
+ l1e[i1].avail1 = nt;
+ ept_p2m_type_to_flags(l1e+i1, nt);
+ }
+ unmap_domain_page(l1e);
+ }
+ unmap_domain_page(l2e);
+ }
+ unmap_domain_page(l3e);
+ }
+ unmap_domain_page(l4e);
+
+ ept_sync_domain(d);
+}
+
void ept_p2m_init(struct domain *d)
{
d->arch.p2m->set_entry = ept_set_entry;
d->arch.p2m->get_entry = ept_get_entry;
d->arch.p2m->get_entry_current = ept_get_entry_current;
+ d->arch.p2m->change_entry_type_global = ept_change_entry_type_global;
}
/*
spin_unlock(&(_p2m)->lock); \
} while (0)
+#define p2m_locked_by_me(_p2m) \
+ (current->processor == (_p2m)->locker)
/* Printouts */
#define P2M_PRINTK(_f, _a...) \
p2m->set_entry = p2m_set_entry;
p2m->get_entry = p2m_gfn_to_mfn;
p2m->get_entry_current = p2m_gfn_to_mfn_current;
+ p2m->change_entry_type_global = p2m_change_type_global;
if ( is_hvm_domain(d) && d->arch.hvm_domain.hap_enabled &&
(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) )
return 0;
}
+void p2m_change_entry_type_global(struct domain *d,
+ p2m_type_t ot, p2m_type_t nt)
+{
+ struct p2m_domain *p2m = d->arch.p2m;
+
+ p2m_lock(p2m);
+ p2m->change_entry_type_global(d, ot, nt);
+ p2m_unlock(p2m);
+}
+
static inline
int set_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn, p2m_type_t p2mt)
{
if ( pagetable_get_pfn(d->arch.phys_table) == 0 )
return;
- p2m_lock(d->arch.p2m);
+ ASSERT(p2m_locked_by_me(d->arch.p2m));
#if CONFIG_PAGING_LEVELS == 4
l4e = map_domain_page(mfn_x(pagetable_get_mfn(d->arch.phys_table)));
unmap_domain_page(l2e);
#endif
- p2m_unlock(d->arch.p2m);
}
/* Modify the p2m type of a single gfn from ot to nt, returning the
void ept_p2m_init(struct domain *d);
+/* EPT violation qualifications definitions */
+/* bit offset 0 in exit qualification */
+#define _EPT_READ_VIOLATION 0
+#define EPT_READ_VIOLATION (1UL<<_EPT_READ_VIOLATION)
+/* bit offset 1 in exit qualification */
+#define _EPT_WRITE_VIOLATION 1
+#define EPT_WRITE_VIOLATION (1UL<<_EPT_WRITE_VIOLATION)
+/* bit offset 2 in exit qualification */
+#define _EPT_EXEC_VIOLATION 2
+#define EPT_EXEC_VIOLATION (1UL<<_EPT_EXEC_VIOLATION)
+
+/* bit offset 3 in exit qualification */
+#define _EPT_EFFECTIVE_READ 3
+#define EPT_EFFECTIVE_READ (1UL<<_EPT_EFFECTIVE_READ)
+/* bit offset 4 in exit qualification */
+#define _EPT_EFFECTIVE_WRITE 4
+#define EPT_EFFECTIVE_WRITE (1UL<<_EPT_EFFECTIVE_WRITE)
+/* bit offset 5 in exit qualification */
+#define _EPT_EFFECTIVE_EXEC 5
+#define EPT_EFFECTIVE_EXEC (1UL<<_EPT_EFFECTIVE_EXEC)
+
+/* bit offset 6 in exit qualification */
+#define _EPT_GAW_VIOLATION 6
+#define EPT_GAW_VIOLATION (1UL<<_EPT_GAW_VIOLATION)
+
+/* bits offset 7 & 8 in exit qualification */
+#define _EPT_GLA_VALIDITY 7
+#define EPT_GLA_VALIDITY_MASK (3UL<<_EPT_GLA_VALIDITY)
+/* gla != gpa, when load PDPTR */
+#define EPT_GLA_VALIDITY_PDPTR_LOAD (0UL<<_EPT_GLA_VALIDITY)
+/* gla != gpa, during guest page table walking */
+#define EPT_GLA_VALIDITY_GPT_WALK (1UL<<_EPT_GLA_VALIDITY)
+/* reserved */
+#define EPT_GLA_VALIDITY_RSVD (2UL<<_EPT_GLA_VALIDITY)
+/* gla == gpa, normal case */
+#define EPT_GLA_VALIDITY_MATCH (3UL<<_EPT_GLA_VALIDITY)
+
+#define EPT_EFFECTIVE_MASK (EPT_EFFECTIVE_READ | \
+ EPT_EFFECTIVE_WRITE | \
+ EPT_EFFECTIVE_EXEC)
+
+#define EPT_PAGETABLE_ENTRIES 512
+
#endif /* __ASM_X86_HVM_VMX_VMX_H__ */
p2m_type_t *p2mt);
mfn_t (*get_entry_current)(unsigned long gfn,
p2m_type_t *p2mt);
+ void (*change_entry_type_global)(struct domain *d,
+ p2m_type_t ot,
+ p2m_type_t nt);
/* Highest guest frame that's ever been mapped in the p2m */
unsigned long max_mapped_pfn;
/* Change types across all p2m entries in a domain */
void p2m_change_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt);
+void p2m_change_entry_type_global(struct domain *d, p2m_type_t ot, p2m_type_t nt);
/* Compare-exchange the type of a single p2m entry */
p2m_type_t p2m_change_type(struct domain *d, unsigned long gfn,